#!/usr/bin/python3
# encoding:utf-8

# 访问API接口
# 生成票号地址 DEMO
# 在 python 3.10.5 测试通过

# pip install bip44    
# pip install requests

from bip32 import BIP32, HARDENED_INDEX
from typing import Iterable, Tuple, Union
from coincurve import PrivateKey, PublicKey
from Crypto.Hash import keccak 

from Crypto import Random
from Crypto.Hash import SHA
from Crypto.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5
from Crypto.Signature import PKCS1_v1_5 as Signature_pkcs1_v1_5
from Crypto.PublicKey import RSA

import time
import base64
import json
import requests

# -------------- 网络访问相关函数 ----------------####----------
# 发送GET请求
def get(api : str) :    
    host = "http://localhost:8888"
    url = host + api  
 
    response = requests.get(url ) 
    print(response.text)    
 

# 发送POST请求 ,from-data（k-v）格式参数
def post(api : str , params = {} ) : 
    host = "http://localhost:8888"
    url = host + api 
     
    response = requests.post(url , data=params )
    print(response.text)    
    
        
# 发送POST请求 , josn格式参数 
def postjson(api : str , params  ) : 
    host = "http://localhost:8888"
    url = host + api      
   
    headers = {"Content-Type": "application/json;charset=utf8" }
    response = requests.post(url , data=json.dumps(params), headers=headers)
    print(response.text)    
    

   
# 加密解密：公钥加密，私钥解密
# 签名验签：私钥签名，公钥验签
# 无论是加密机密还是签名验签都使用同一对秘钥对

# 签名
def sign(message) :
    #  使用 client 私钥对内容进行签名，然后再使用base64编码
    with open('client-private.pem') as f:
        key = f.read()
        rsakey = RSA.importKey(key)
        signer = Signature_pkcs1_v1_5.new(rsakey)
        digest = SHA.new()       
        
        # 把STR字符串编码为 UTF-8
        message = message.encode('UTF-8')    
        digest.update(message)
        sign = signer.sign(digest)
        signature = base64.b64encode(sign)
        return signature            

# ###------------------- ### ---------------------
# keccak
def keccak_256(b: bytes) -> bytes:
    h = keccak.new(data=b, digest_bits=256)
    return h.digest()


# 检查ETH格式的地址
def to_checksum_addr(eth_addr: str) -> str:
    """
    Convert eth address to eth checksum address.

    EIP 55: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-55.md#implementation
    """
    address = eth_addr.lower().replace("0x", "")
    addr_hash = keccak_256(address.encode()).hex()

    res = []
    for a, h in zip(address, addr_hash):
        if int(h, 16) >= 8:
            res.append(a.upper())
        else:
            res.append(a)

    return "0x" + "".join(res)


# 用公钥生成ETH格式的地址
def get_eth_addr(pk: Union[str, bytes]) -> str:
    """Get ETH address from a public key."""
    pk_bytes = bytes.fromhex(pk) if isinstance(pk, str) else pk

    if len(pk_bytes) != 64:
        pk_bytes = PublicKey(pk_bytes).format(False)[1:]

    return to_checksum_addr(f"0x{keccak_256(pk_bytes)[-20:].hex()}")
    
    
# 供票方的每一只票的根公钥从企业端的管理后台或者从【接口API】获得
def gen_token(xpub_root ,nAddrCount ) :
    # 以下描述的是上票方生成票号的算法 
    # print(f"xpub: {xpub_root}")   
    # print()  
  
    bip44 = BIP32.from_xpub(xpub_root)    
 
    # 因为 xpub_root 是以路径 m/44'/60'/1'/0 为根，仅在这基础上加序号做扩展，因此 get_pubkey_from_path函数用的路径为 m/序号
    
    # 生成多张票的地址，票据的序号从1开始
    token_dict = {}
    i = 0
    while i < nAddrCount-1:
        i += 1
          
        this_dict = {}
        this_dict["sequence"] = str(i)
        pk_i =  bip44.get_pubkey_from_path("m/"+ str(i)) 
        addr_i =  get_eth_addr(pk_i)
        this_dict["token"] = addr_i
        
        addr_sign = sign(addr_i) 
        addr_sign = addr_sign.decode('UTF-8')         
        this_dict["sign"] = addr_sign
        token_dict[i] = this_dict
    return token_dict
        

# ########## ------------------- ############## ---- 供票方 ---------
# 主程序入口
if __name__ == '__main__': 
    
    # ####
    # 票据发行的接口
    # api_url = "/issue"
    # xpub_root = "xpub6EwLt1EDyThtuQV58UroAigCzRi8iwMRTSSs2peNiSwr2qTR1ffXEPYiGhMJ3pwSx8RKfaABjGhsu9raX27shVVzeDkZSRiz3JFhPCm6Czf"  
    # nAddrCount = 10   
    # 需要生成多张票据  
    # data = gen_token(xpub_root ,nAddrCount)
    # print(data)
    # #### 
    
    # 检查用户是否存在接口
    api_url = "/users"
    
    data = {"request":[{"mobile":"17683853671","id":"440402197101020015","name":"测试人"},{"mobile":"17683853672","id":"440402197101020016","name":"测试二"}]}
    

    # post 方式访问，josn格式参数
    # 接口url 路径 /api
   
    appid = "1160fc300abce69590556fbb0acec8ca"  
 
    # 发送消息的时间戳 
    # 年月日时分秒+时间戳10位秒    
    # timestamp = time.strftime("%Y%m%d%H%M%S",time.localtime())+str(int(time.time())) 
    timestamp = str(int(time.time()))
    # print(f"time_send:{timestamp}")
    
    # 签名方式为 sign(appid+time_stamp)
    message = str(appid) + str(timestamp)    
    signature = sign(message).decode('UTF-8')
    
    try:
        postjson(api_url ,{ "timestamp" : timestamp ,"appid" : appid ,"signature" : signature ,"data" : data } )
    except:
        print("服务端没开机或者网络故障")